package com.treasure.mall.biz.service.core.order.impl;

import com.treasure.mall.biz.base.AbstractSearchableCrudService;
import com.treasure.mall.biz.base.AuthHelper;
import com.treasure.mall.biz.base.SpecificationAndSort;
import com.treasure.mall.biz.base.UserDetails;
import com.treasure.mall.biz.dto.BaseSaveDTO;
import com.treasure.mall.biz.dto.BaseSearchDTO;
import com.treasure.mall.biz.dto.condition.AfterSaleCondition;
import com.treasure.mall.biz.dto.order.AfterSaleSaveDTO;
import com.treasure.mall.biz.dto.user.ModifyBalanceDTO;
import com.treasure.mall.biz.entity.embeddable.GoodsInfo_;
import com.treasure.mall.biz.entity.order.*;
import com.treasure.mall.biz.entity.pay.PaymentBillPO;
import com.treasure.mall.biz.entity.pay.RefundBillPO;
import com.treasure.mall.biz.entity.user.MerchantPO_;
import com.treasure.mall.biz.entity.user.UserPO;
import com.treasure.mall.biz.entity.user.UserPO_;
import com.treasure.mall.biz.remote.quickpay.WechatPayService;
import com.treasure.mall.biz.remote.quickpay.dto.WxRefundDTO;
import com.treasure.mall.biz.repository.order.AfterSaleRepository;
import com.treasure.mall.biz.repository.order.OrderItemRepository;
import com.treasure.mall.biz.service.core.order.*;
import com.treasure.mall.biz.service.core.pay.PaymentBillService;
import com.treasure.mall.biz.service.core.pay.RefundBillService;
import com.treasure.mall.biz.service.core.user.MerchantService;
import com.treasure.mall.biz.service.core.user.UserService;
import com.treasure.mall.zbq.common.Jsr310Utils;
import com.treasure.mall.zbq.common.MathUtils;
import com.treasure.mall.zbq.common.RandomUtils;
import com.treasure.mall.zbq.common.base.ApiResult;
import com.treasure.mall.zbq.common.base.ResultCodeEnum;
import com.treasure.mall.zbq.common.exception.ApiException;
import com.treasure.mall.zbq.common.ienum.base.CodeDescEnumHelper;
import com.treasure.mall.zbq.common.ienum.log.AdvanceLogType;
import com.treasure.mall.zbq.common.ienum.order.OrderEnums;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author allan
 * @version 1.0.0
 * @date 2020/6/11
 */
@Service
public class AfterSaleServiceImpl extends AbstractSearchableCrudService<AfterSaleBillPO, String> implements AfterSaleService {
    @Autowired
    private UserService userService;
    @Autowired
    private OrderItemRepository orderItemRepository;
    @Autowired
    private RefundBillService refundBillService;
    @Autowired
    private WechatPayService wechatPayService;
    @Autowired
    private MerchantService merchantService;
    @Autowired
    private SubOrderService subOrderService;
    @Autowired
    private PaymentBillService paymentBillService;
    private AfterSaleRepository afterSaleRepository;
    @Autowired
    private BizOrderService bizOrderService;
    @Autowired
    private RebateBillService rebateBillService;
    @Autowired
    private SettlementBillService settlementBillService;

    @Autowired
    protected AfterSaleServiceImpl(AfterSaleRepository repository) {
        super(repository);
        this.afterSaleRepository = repository;
    }

    @Override
    protected SpecificationAndSort<AfterSaleBillPO> specificationAndSort(BaseSearchDTO searchDTO) {
        AfterSaleCondition condition = (AfterSaleCondition) searchDTO;
        UserDetails userDetails = AuthHelper.getPrincipal();
        if (AuthHelper.isMerchant(userDetails)) {
            condition.setMerchantId(userDetails.getId());
        }

        Specification<AfterSaleBillPO> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (!StringUtils.isEmpty(condition.getGoodsKey())) {
                predicates.add(cb.or(
                        cb.like(root.get(AfterSaleBillPO_.goodsInfo).get(GoodsInfo_.goodsBn), likeAll(condition.getGoodsKey())),
                        cb.like(root.get(AfterSaleBillPO_.goodsInfo).get(GoodsInfo_.goodsName), likeAll(condition.getGoodsKey()))
                ));
            }

            if (!StringUtils.isEmpty(condition.getUserKey())) {
                predicates.add(cb.or(
                        cb.like(root.get(AfterSaleBillPO_.userPO).get(UserPO_.mobile), likeAll(condition.getUserKey())),
                        cb.like(root.get(AfterSaleBillPO_.userPO).get(UserPO_.realName), likeAll(condition.getUserKey())),
                        cb.like(root.get(AfterSaleBillPO_.userPO).get(UserPO_.nickName), likeAll(condition.getUserKey()))
                ));
            }
            if (condition.getStatus() != -1) {
                OrderEnums.AfterSaleStatus afterSaleStatus = CodeDescEnumHelper.getEnumTypeByCode(OrderEnums.AfterSaleStatus.class, condition.getStatus());
                predicates.add(cb.equal(root.get(AfterSaleBillPO_.afterSaleStatus), afterSaleStatus));
            }
            if (condition.getUserId() != null) {
                predicates.add(cb.equal(root.get(AfterSaleBillPO_.userPO).get(UserPO_.userId), condition.getUserId()));
            }

            if (condition.getMerchantId() > 0) {
                predicates.add(cb.equal(root.get(AfterSaleBillPO_.merchantPO).get(MerchantPO_.merchantId), condition.getMerchantId()));
            }

            if (!StringUtils.isEmpty(condition.getId())) {
                predicates.add(cb.equal(root.get(AfterSaleBillPO_.id), condition.getId()));
            }
            if (!StringUtils.isEmpty(condition.getSubOrderId())) {
                predicates.add(cb.equal(root.get(AfterSaleBillPO_.subOrderId), condition.getSubOrderId()));
            }

            if (!StringUtils.isEmpty(condition.getBeginTime())) {
                LocalDateTime beginTime = Jsr310Utils.DateTime.of(condition.getBeginTime());
                predicates.add(cb.greaterThanOrEqualTo(root.get(AfterSaleBillPO_.createTime), beginTime));
            }
            if (!StringUtils.isEmpty(condition.getEndTime())) {
                LocalDateTime endTime = Jsr310Utils.DateTime.of(condition.getEndTime());
                predicates.add(cb.lessThanOrEqualTo(root.get(AfterSaleBillPO_.createTime), endTime));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Sort sort = new Sort(Sort.Direction.DESC, "createTime");

        return new SpecificationAndSort<>(specification, sort);
    }

    @Override
    public AfterSaleBillPO save(BaseSaveDTO saveDTO) {
        AfterSaleSaveDTO afterSaleSaveDTO = (AfterSaleSaveDTO) saveDTO;
        OrderItemPO orderItemPO = orderItemRepository.findById(afterSaleSaveDTO.getOrderItemId()).get();
        if (!orderItemPO.enableAfterSale()) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.BIZ_ERROR, "当前无法进行售后"));
        }
        BigDecimal totalAmount = orderItemPO.refundAmount();

        UserPO userPO = userService.findById(afterSaleSaveDTO.getUserId());

        AfterSaleBillPO afterSaleBillPO = new AfterSaleBillPO();
        afterSaleBillPO.setId(RandomUtils.randomFlowNo());
        afterSaleBillPO.setPayType(orderItemPO.getSubOrderPO().getOrderPO().getPayType());
        afterSaleBillPO.setSubOrderId(orderItemPO.getSubOrderPO().getSubOrderId());
        afterSaleBillPO.setOrderItemId(orderItemPO.getId());
        afterSaleBillPO.setGoodsInfo(orderItemPO.getGoodsInfo());
        afterSaleBillPO.setUserPO(userPO);
        afterSaleBillPO.setMerchantPO(orderItemPO.getSubOrderPO().getMerchantPO());
        afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.APPLYING);
        afterSaleBillPO.setTotalAmount(totalAmount);
        afterSaleBillPO.setReason(afterSaleSaveDTO.getReason());
        afterSaleBillPO.setUserMemo(afterSaleSaveDTO.getUserMemo());
        afterSaleBillPO.setRemark(afterSaleSaveDTO.getRemark());
        afterSaleBillPO.setImages(afterSaleSaveDTO.getImages());
        afterSaleBillPO.setFatherRebate(orderItemPO.getFatherRebate());
        afterSaleBillPO.setGrandRebate(orderItemPO.getGrandRebate());
        afterSaleBillPO.setPayment(orderItemPO.getPayment());
        afterSaleBillPO.setAfterSaleType(CodeDescEnumHelper.getEnumTypeByCode(OrderEnums.AfterSaleType.class, afterSaleSaveDTO.getAfterSaleType()));

        afterSaleBillPO = repository.save(afterSaleBillPO);

        orderItemPO.setAfterSaleStatus(afterSaleBillPO.getAfterSaleStatus());
        orderItemPO.setAfterSaleId(afterSaleBillPO.getId());
        orderItemRepository.save(orderItemPO);
        //设置结算单失效状态
        settlementBillService.modifyInvalidate(afterSaleBillPO.getOrderItemId(), true);
        rebateBillService.modifyInvalidate(afterSaleBillPO.getOrderItemId(), true);
        return afterSaleBillPO;
    }

    @Override
    @Transactional
    public void approve(String id) {
        AfterSaleBillPO afterSaleBillPO = findById(id);
        if (afterSaleBillPO.getAfterSaleStatus() != OrderEnums.AfterSaleStatus.APPLYING) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "请检查售后状态"));
        }
        if (afterSaleBillPO.getAfterSaleType() == OrderEnums.AfterSaleType.REFUND) {
            afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.TO_REFUND);
        } else {
            afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.WAIT_BUYER_DELIVER_GOODS);
        }
        repository.save(afterSaleBillPO);

        //修改orderItem的状态
        orderItemRepository.updateAfterSale(afterSaleBillPO.getOrderItemId(), afterSaleBillPO.getAfterSaleStatus());
    }

    @Override
    @Transactional
    public void cancel(String id) {
        AfterSaleBillPO afterSaleBillPO = findById(id);
        if (afterSaleBillPO.getAfterSaleStatus() != OrderEnums.AfterSaleStatus.APPLYING) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "请检查售后状态"));
        }
        afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.CANCEL_APPLY);
        repository.save(afterSaleBillPO);

        OrderItemPO orderItemPO = orderItemRepository.findById(afterSaleBillPO.getOrderItemId()).get();
        orderItemPO.setAfterSaleStatus(null);
        orderItemPO.setAfterSaleId(null);
        orderItemRepository.save(orderItemPO);
        //设置结算单失效状态
        settlementBillService.modifyInvalidate(afterSaleBillPO.getOrderItemId(), false);
        rebateBillService.modifyInvalidate(afterSaleBillPO.getOrderItemId(), false);
    }

    /**
     * 退款之前，先检查一下对应的用户或商户账号上的余额是否充足
     *
     * @param orderItemPO
     */
    private void checkRefundAmount(OrderItemPO orderItemPO) {
        if (orderItemPO == null) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "当前无退款记录"));
        }
        int successFlag = 0;
        String moible = "";
        //获取佣金记录
        List<RebateBillPO> rebateBillPOS = rebateBillService.findByOrderItem(orderItemPO.getId());
        for (RebateBillPO rebateBillPO : rebateBillPOS) {
            moible = rebateBillPO.getUserPO().getMobile();
            if (rebateBillPO.isConverted()) {
                if (MathUtils.less(rebateBillPO.getUserPO().getBalance(), rebateBillPO.getAmount())) {
                    successFlag = 1;
                    break;
                }
            } else {
                if (MathUtils.less(rebateBillPO.getUserPO().getBalanceInFuture(), rebateBillPO.getAmount())) {
                    successFlag = 2;
                    break;
                }
            }
        }
        if (successFlag > 0) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, successFlag == 1 ? "用户[" + moible + "]余额不足" : "用户[" + moible + "]待入账余额不足"));
        }
        //获取货款记录
        SettlementBillPO settlementBillPO = settlementBillService.findByOrderItem(orderItemPO.getId());
        if (settlementBillPO.isConverted()) {
            if (MathUtils.less(settlementBillPO.getMerchantPO().getBalance(), settlementBillPO.getAmount())) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "商户[" + settlementBillPO.getMerchantPO().getUsername() + "]货款不足"));
            }
        } else {
            if (MathUtils.less(settlementBillPO.getMerchantPO().getBalanceInFuture(), settlementBillPO.getAmount())) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "商户[" + settlementBillPO.getMerchantPO().getUsername() + "]待入账货款不足"));
            }
        }
    }

    @Override
    @Transactional
    public void refund(String id, int refundType) {
        AfterSaleBillPO afterSaleBillPO = findById(id);
        OrderEnums.RefundType refundTypeEnum = CodeDescEnumHelper.getEnumTypeByCode(OrderEnums.RefundType.class, refundType);
        if (!afterSaleBillPO.enableRefund()) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "请检查售后状态"));
        }
        OrderItemPO orderItemPO = orderItemRepository.findById(afterSaleBillPO.getOrderItemId()).get();

        //判断是否超过当时支付金额
        this.checkRefundAmount(orderItemPO);
        //要退款的余额
        afterSaleBillPO.setFinalAmount(afterSaleBillPO.getTotalAmount());
        BigDecimal refundBalance = afterSaleBillPO.getFinalAmount();
        //如果原路返回，计算要退款的余额
        if (refundTypeEnum == OrderEnums.RefundType.ORIGINAL_WAY) {
            if (MathUtils.less(orderItemPO.getSubOrderPO().getOrderPO().getBalance(), afterSaleBillPO.getFinalAmount())) {
                refundBalance = orderItemPO.getSubOrderPO().getOrderPO().getBalance();
            }
        }
        afterSaleBillPO.setRefundBalance(refundBalance);
        afterSaleBillPO.setRefundType(refundTypeEnum);
        afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.WAIT_SELLER_REFUND);

        if (MathUtils.greater(refundBalance, BigDecimal.ZERO)) {
            //生成余额退款单
            refundBillService.create(afterSaleBillPO, refundBalance, OrderEnums.RefundType.TO_BALANCE);
        }

        //第三方待退
        BigDecimal surplusRefundAmount = afterSaleBillPO.getFinalAmount().subtract(refundBalance);
        if (MathUtils.greater(surplusRefundAmount, BigDecimal.ZERO)) {
            //生成第三方退款单
            RefundBillPO refundBillPO = refundBillService.create(afterSaleBillPO, surplusRefundAmount, OrderEnums.RefundType.ORIGINAL_WAY);

            //发起退款
            if (refundBillPO.getPayType() == OrderEnums.PayType.WECHAT_PAY) {
                WxRefundDTO wxRefundDTO = new WxRefundDTO();
                PaymentBillPO paymentBillPO = paymentBillService.getThirdPayBill(orderItemPO.getSubOrderPO().getOrderPO().getOrderId());
                wxRefundDTO.setOrderNo(paymentBillPO.getId());
                wxRefundDTO.setRefundFee(refundBillPO.getAmount());
                wxRefundDTO.setTotalFee(refundBillPO.getAmount());
                wxRefundDTO.setRefundNo(refundBillPO.getId());
                boolean success = wechatPayService.wxRefund(wxRefundDTO);
                if (!success) {
                    throw new ApiException(ApiResult.errorWith(ResultCodeEnum.THIRD_ERROR, "请求微信退款接口失败"));
                }
            }
            repository.save(afterSaleBillPO);

            orderItemRepository.updateAfterSale(afterSaleBillPO.getOrderItemId(), afterSaleBillPO.getAfterSaleStatus());
        } else {
            //退款成功
            complete(afterSaleBillPO);
        }
    }

    @Override
    @Transactional
    public void complete(String refundId, String thirdNo, boolean isSuccess, String resultDesc, String creditInfo) {
        //修改退款单
        RefundBillPO refundBillPO = refundBillService.findById(refundId);
        if (refundBillPO.isSuccess()) {
            //退款单已经处理成功的，直接返回
            return;
        }

        refundBillPO.setSuccess(isSuccess);
        refundBillPO.setArriveTime(LocalDateTime.now());
        refundBillPO.setResultDesc(resultDesc);
        refundBillPO.setThirdRefundNo(thirdNo);
        refundBillPO.setCreditInfo(creditInfo);
        refundBillService.save(refundBillPO);
        if (isSuccess) {
            if (refundBillPO.getDealType() == OrderEnums.DealType.ORDER) {
                complete(refundBillPO.getAfterSaleId());
            } else {
                bizOrderService.refundComplete(refundBillPO.getOrderId());
            }
        }
    }

    @Override
    @Transactional
    public void complete(String afterSaleId) {
        AfterSaleBillPO afterSaleBillPO = findById(afterSaleId);

        complete(afterSaleBillPO);
    }

    @Override
    @Transactional
    public void complete(AfterSaleBillPO afterSaleBillPO) {
        //得到账户退款单
        List<RefundBillPO> refundBillPOS = refundBillService.findUserRefund(afterSaleBillPO.getSubOrderId());
        for (RefundBillPO refundBillPO : refundBillPOS) {
            refundBillPO.setSuccess(true);
            refundBillPO.setArriveTime(LocalDateTime.now());
            refundBillService.save(refundBillPO);
        }

        afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.COMPLETE_APPLY);

        repository.save(afterSaleBillPO);

        SubOrderPO subOrderPO = subOrderService.findById(afterSaleBillPO.getSubOrderId());
        OrderItemPO orderItemPO = subOrderPO.getOrderItemPOS().stream()
                .filter(p -> p.getId().equals(afterSaleBillPO.getOrderItemId())).findAny().get();

        orderItemPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.COMPLETE_APPLY);
        orderItemPO.setAfterSaleFinishTime(LocalDateTime.now());
        if (orderItemPO.getSubOrderPO().isOrderClose() && orderItemPO.getSubOrderPO().getStatus() != OrderEnums.Status.DONE) {
            //交易关闭
            orderItemPO.getSubOrderPO().setStatus(OrderEnums.Status.CLOSED);
            subOrderService.save(orderItemPO.getSubOrderPO());
        }

        //余额加上去
        if (MathUtils.greater(afterSaleBillPO.getRefundBalance(), BigDecimal.ZERO)) {
            ModifyBalanceDTO balanceDTO = new ModifyBalanceDTO();
            balanceDTO.setAmount(afterSaleBillPO.getRefundBalance());
            balanceDTO.setId(afterSaleBillPO.getUserPO().getUserId());
            balanceDTO.setOrderId(afterSaleBillPO.getSubOrderId());
            balanceDTO.setLogType(AdvanceLogType.LOG_TYPE1);
            userService.modifyBalance(balanceDTO);
        }

        //扣减佣金
        List<RebateBillPO> rebateBillPOS = rebateBillService.findByOrderItem(orderItemPO.getId());
        ModifyBalanceDTO balanceDTO = new ModifyBalanceDTO();
        balanceDTO.setOrderId(afterSaleBillPO.getSubOrderId());

        for (RebateBillPO rebateBillPO : rebateBillPOS) {
            balanceDTO.setAmount(rebateBillPO.getAmount().negate());
            balanceDTO.setId(rebateBillPO.getUserPO().getUserId());
            if (rebateBillPO.isConverted()) {
                //扣除转正的
                balanceDTO.setLogType(AdvanceLogType.LOG_TYPE);
                userService.modifyBalance(balanceDTO);
            } else {
                //扣除带转正的
                balanceDTO.setLogType(AdvanceLogType.LOG_TYPE4);
                userService.modifyBalanceInFuture(balanceDTO);
            }
            rebateBillPO.setInvalidate(true);

            rebateBillService.save(rebateBillPO);
        }

        //扣减货款
        SettlementBillPO settlementBillPO = settlementBillService.findByOrderItem(orderItemPO.getId());
        balanceDTO.setId(settlementBillPO.getMerchantPO().getId());
        balanceDTO.setAmount(settlementBillPO.getAmount().negate());

        if (settlementBillPO.isConverted()) {
            //扣减转正的
            balanceDTO.setLogType(AdvanceLogType.LOG_TYPE6);
            merchantService.modifyBalance(balanceDTO);
        } else {
            //扣减待转正的
            balanceDTO.setLogType(AdvanceLogType.LOG_TYPE11);
            merchantService.modifyBalanceInFuture(balanceDTO);
        }
        settlementBillPO.setInvalidate(true);
        settlementBillService.save(settlementBillPO);
    }

    @Override
    @Transactional
    public void refuse(String id, String remark) {
        AfterSaleBillPO afterSaleBillPO = findById(id);
        if (afterSaleBillPO.getAfterSaleStatus() != OrderEnums.AfterSaleStatus.APPLYING) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "请检查售后状态"));
        }
        afterSaleBillPO.setAfterSaleStatus(OrderEnums.AfterSaleStatus.REFUSE_APPLY);
        repository.save(afterSaleBillPO);

        orderItemRepository.updateAfterSale(afterSaleBillPO.getOrderItemId(), OrderEnums.AfterSaleStatus.REFUSE_APPLY);

        //设置结算单失效状态
        settlementBillService.modifyInvalidate(afterSaleBillPO.getOrderItemId(), false);
        rebateBillService.modifyInvalidate(afterSaleBillPO.getOrderItemId(), false);
    }
}
